package ru.spb.osll.myfit.common;

import com.google.gwt.user.client.rpc.IsSerializable;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * Created by IntelliJ IDEA.
 * User: Vassar
 * Date: 04.03.2011
 * Time: 12:33:29
 * To change this template use File | Settings | File Templates.
 */
public class TreeNode implements Serializable, IsSerializable {
    private static final long serialVersionUID = 3L;
    private TreeNode parent = null;
    private ArrayList children = null;
    private String reference;

    /**
     * cTtor
     *
     * @param obj referenced object
     */
    public TreeNode(String obj) {
        this.parent = null;
        this.reference = obj;
        this.children = new ArrayList();
    }

    public TreeNode() {
    }

    /**
     * remove node from tree
     */
    public void remove() {
        if (parent != null) {
            parent.removeChild(this);
        }
    }

    /**
     * remove child node
     *
     * @param child
     */
    private void removeChild(TreeNode child) {
        if (children.contains(child))
            children.remove(child);

    }

    /**
     * add child node
     *
     * @param child node to be added
     */
    public void addChildNode(TreeNode child) {
        child.parent = this;
        if (!children.contains(child))
            children.add(child);
    }

    /**
     * deep copy (clone)
     *
     * @return copy of TreeNode
     */
    public TreeNode deepCopy() {
        TreeNode newNode = new TreeNode(reference);
        for (Iterator iter = children.iterator(); iter.hasNext();) {
            TreeNode child = (TreeNode) iter.next();
            newNode.addChildNode(child.deepCopy());
        }
        return newNode;
    }

    /**
     * deep copy (clone) and prune
     *
     * @param depth - number of child levels to be copied
     * @return copy of TreeNode
     */
    public TreeNode deepCopyPrune(int depth) {
        if (depth < 0)
            throw new IllegalArgumentException("Depth is negative");
        TreeNode newNode = new TreeNode(reference);
        if (depth == 0)
            return newNode;
        for (Iterator iter = children.iterator(); iter.hasNext();) {
            TreeNode child = (TreeNode) iter.next();
            newNode.addChildNode(child.deepCopyPrune(depth - 1));
        }
        return newNode;
    }

    /**
     * @return level = distance from root
     */
    public int getLevel() {
        int level = 0;
        TreeNode p = parent;
        while (p != null) {
            ++level;
            p = p.parent;
        }
        return level;
    }

    /**
     * walk through subtree of this node
     *
     * @param callbackHandler function called on iteration
     */
    public int walkTree(TreeNodeCallback callbackHandler) {
        int code = 0;
        code = callbackHandler.handleTreeNode(this);
        if (code != TreeNodeCallback.CONTINUE)
            return code;
        ChildLoop:
        for (Iterator iter = children.iterator(); iter.hasNext();) {
            TreeNode child = (TreeNode) iter.next();
            code = child.walkTree(callbackHandler);
            if (code >= TreeNodeCallback.CONTINUE_PARENT)
                return code;
        }
        return code;
    }

    /**
     * walk through children subtrees of this node
     *
     * @param callbackHandler function called on iteration
     */
    public int walkChildren(TreeNodeCallback callbackHandler) {
        int code = 0;
        ChildLoop:
        for (Iterator iter = children.iterator(); iter.hasNext();) {
            TreeNode child = (TreeNode) iter.next();
            code = callbackHandler.handleTreeNode(child);
            if (code >= TreeNodeCallback.CONTINUE_PARENT)
                return code;
            if (code == TreeNodeCallback.CONTINUE) {
                code = child.walkChildren(callbackHandler);
                if (code > TreeNodeCallback.CONTINUE_PARENT)
                    return code;
            }
        }
        return code;
    }

    /**
     * @return List of children
     */
    public List getChildren() {
        return children;
    }

    /**
     * @return parent node
     */
    public TreeNode getParent() {
        return parent;
    }

    /**
     * @return reference object
     */
    public String getReference() {
        return reference;
    }

    /**
     * set reference object
     *
     * @param object reference
     */
    public void setReference(String object) {
        reference = object;
    }
}

interface TreeNodeCallback {

  public static final int CONTINUE = 0;
  public static final int CONTINUE_SIBLING = 1;
  public static final int CONTINUE_PARENT = 2;
  public static final int BREAK = 3;

  /**
   * @param node the current node to handle
   * @return 0 continue tree walk
   *         1 break this node (continue sibling)
   *         2 break this level (continue parent level)
   *         3 break tree walk
   */
  int handleTreeNode(TreeNode node);
} // TreeNodeCallback
